#include "stdafx.h"

//--------------------------------------------------------------------------------------
typedef struct _ACE_HEADER 
{
	UCHAR  AceType;
	UCHAR  AceFlags;
	USHORT AceSize;

} ACE_HEADER;
typedef ACE_HEADER *PACE_HEADER;

typedef struct _ACCESS_ALLOWED_ACE 
{
	ACE_HEADER Header;
	ACCESS_MASK Mask;
	ULONG SidStart;

} ACCESS_ALLOWED_ACE;

//#ifndef __wtypes_h__
//
//typedef struct _SID 
//{
//	UCHAR  Revision;
//	UCHAR  SubAuthorityCount;
//	SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
//
//	ULONG SubAuthority[ANYSIZE_ARRAY];
//
//} SID, *PISID;
//
//#endif // __wtypes_h__

NTSYSAPI 
	NTSTATUS
	NTAPI
	RtlAddAccessAllowedAce(
	PACL  Acl,
	ULONG  AceRevision,
	ACCESS_MASK  AccessMask,
	PSID  Sid
	);
NTSYSAPI 
	NTSTATUS
	NTAPI
	RtlSelfRelativeToAbsoluteSD2(
	PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor,
	PULONG pBufferSize
	); 
BOOLEAN SetObjectSecurityWorld(HANDLE hObject, ACCESS_MASK AccessMask)
{
	BOOLEAN bRet = FALSE;
	ULONG SdLength = 0;
	PSECURITY_DESCRIPTOR Descr = NULL;
	// query security descriptor length
	NTSTATUS ns = ZwQuerySecurityObject(hObject, DACL_SECURITY_INFORMATION, NULL, 0, &SdLength); 
	if (ns != STATUS_BUFFER_TOO_SMALL) 
	{ 
		DbgMsg(__FILE__, __LINE__, "ZwQuerySecurityObject() fails; status: 0x%.8x\n", ns);
		return FALSE; 
	} 

	// allocate memory for security descriptor
	Descr = (PSECURITY_DESCRIPTOR)M_ALLOC(SdLength);
	if (Descr)
	{
		// query security descriptor
		ns = ZwQuerySecurityObject(hObject, DACL_SECURITY_INFORMATION, Descr, SdLength, &SdLength); 
		if (NT_SUCCESS(ns)) 
		{
			BOOLEAN DaclPresent = FALSE, DaclDefaulted = FALSE;
			PACL OldDacl = NULL;

			// get descriptor's DACL
			ns = RtlGetDaclSecurityDescriptor(
				Descr, 
				&DaclPresent, 
				&OldDacl, 
				&DaclDefaulted
				); 
			if (NT_SUCCESS(ns)) 
			{ 
#define SID_REVISION (1)    // Current revision level

#define SECURITY_WORLD_SID_AUTHORITY {0,0,0,0,0,1}
#define SECURITY_WORLD_RID (0x00000000L)

				SID Sid;
				SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_WORLD_SID_AUTHORITY;  

				RtlZeroMemory(&Sid, sizeof(Sid));

				// initialize SID
				Sid.Revision = SID_REVISION;
				Sid.SubAuthorityCount = 1;
				Sid.IdentifierAuthority = SidAuth;
				Sid.SubAuthority[0] = SECURITY_WORLD_RID;

				if (RtlValidSid(&Sid)) 
				{
					// calculate new DACL size
					PACL NewDacl=NULL;
					ULONG NewDaclSize = sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(&Sid);  
					if (DaclPresent && OldDacl)
					{
						NewDaclSize += OldDacl->AclSize;
					}

					// allocate new DACL
					NewDacl = (PACL)M_ALLOC(NewDaclSize);
					if (NewDacl)
					{
						// copy current DACL
						RtlCopyMemory(NewDacl, OldDacl, OldDacl->AclSize); 
						NewDacl->AclSize = (USHORT)NewDaclSize; 

						ns = RtlAddAccessAllowedAce(NewDacl, ACL_REVISION, AccessMask, &Sid); 
						if (NT_SUCCESS(ns))
						{
							ns = RtlSelfRelativeToAbsoluteSD2(Descr, &SdLength); 
							if (NT_SUCCESS(ns)) 
							{ 
								// update descriptor's DACL
								ns = RtlSetDaclSecurityDescriptor(Descr, TRUE, NewDacl, DaclDefaulted); 
								if (NT_SUCCESS(ns)) 
								{ 
									// set new security descriptor
									ns = ZwSetSecurityObject(hObject, DACL_SECURITY_INFORMATION, Descr); 
									if (NT_SUCCESS(ns)) 
									{ 
										bRet = TRUE;
									} 
									else
									{
										DbgMsg(__FILE__, __LINE__, "ZwSetSecurityObject() fails; status: 0x%.8x\n", ns);
									}
								} 
								else
								{
									DbgMsg(__FILE__, __LINE__, "RtlSetDaclSecurityDescriptor() fails; status: 0x%.8x\n", ns);
								}   
							}
							else
							{
								DbgMsg(__FILE__, __LINE__, "RtlSelfRelativeToAbsoluteSD2() fails; status: 0x%.8x\n", ns);
							}                            
						}
						else
						{
							DbgMsg(__FILE__, __LINE__, "RtlAddAccessAllowedAce() fails; status: 0x%.8x\n", ns);
						}

						M_FREE(NewDacl);
					}
					else
					{
						DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
					}
				} 
			} 
			else
			{
				DbgMsg(__FILE__, __LINE__, "RtlGetDaclSecurityDescriptor() fails; status: 0x%.8x\n", ns);
			}
		}
		else
		{ 
			DbgMsg(__FILE__, __LINE__, "ZwQuerySecurityObject() fails; status: 0x%.8x\n", ns);
		} 

		M_FREE(Descr);
	}
	else
	{
		DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
	}

	return bRet;
}
//--------------------------------------------------------------------------------------

PVOID KernelGetModuleBase(char *ModuleName)
{
	PVOID pModuleBase = NULL;
	UNICODE_STRING usCommonHalName, usCommonNtName;
#define HAL_NAMES_NUM 6
	wchar_t *wcHalNames[] = 
	{
		L"hal.dll",      // Non-ACPI PIC HAL 
		L"halacpi.dll",  // ACPI PIC HAL
		L"halapic.dll",  // Non-ACPI APIC UP HAL
		L"halmps.dll",   // Non-ACPI APIC MP HAL
		L"halaacpi.dll", // ACPI APIC UP HAL
		L"halmacpi.dll"  // ACPI APIC MP HAL
	};

#define NT_NAMES_NUM 4
	wchar_t *wcNtNames[] = 
	{
		L"ntoskrnl.exe", // UP
		L"ntkrnlpa.exe", // UP PAE
		L"ntkrnlmp.exe", // MP
		L"ntkrpamp.exe"  // MP PAE
	};
	PRTL_PROCESS_MODULES Info = (PRTL_PROCESS_MODULES)GetSysInf(SystemModuleInformation);
	RtlInitUnicodeString(&usCommonHalName, L"hal.dll");
	RtlInitUnicodeString(&usCommonNtName, L"ntoskrnl.exe");
	if (Info)
	{
		ANSI_STRING asModuleName;
		UNICODE_STRING usModuleName;
		NTSTATUS ns;
		RtlInitAnsiString(&asModuleName, ModuleName);
		ns = RtlAnsiStringToUnicodeString(&usModuleName, &asModuleName, TRUE);
		if (NT_SUCCESS(ns))
		{
			ULONG i=0;
			for (i = 0; i < Info->NumberOfModules; i++)
			{
				ANSI_STRING asEnumModuleName;
				UNICODE_STRING usEnumModuleName;
				NTSTATUS ns;
				RtlInitAnsiString(
					&asEnumModuleName, 
					(char *)Info->Modules[i].FullPathName + Info->Modules[i].OffsetToFileName
					);

				ns = RtlAnsiStringToUnicodeString(&usEnumModuleName, &asEnumModuleName, TRUE);
				if (NT_SUCCESS(ns))
				{                    
					if (RtlEqualUnicodeString(&usModuleName, &usCommonHalName, TRUE))
					{
						// hal.dll passed as module name
						int i_m=0;
						for (i_m = 0; i_m < HAL_NAMES_NUM; i_m++)
						{
							UNICODE_STRING usHalName;
							RtlInitUnicodeString(&usHalName, wcHalNames[i_m]);

							// compare module name from list with known HAL module name
							if (RtlEqualUnicodeString(&usEnumModuleName, &usHalName, TRUE))
							{
								pModuleBase = (PVOID)Info->Modules[i].ImageBase;
								break;
							}
						}
					}
					else if (RtlEqualUnicodeString(&usModuleName, &usCommonNtName, TRUE))
					{
						// ntoskrnl.exe passed as module name
						int i_m=0;
						for (i_m = 0; i_m < NT_NAMES_NUM; i_m++)
						{
							UNICODE_STRING usNtName;
							RtlInitUnicodeString(&usNtName, wcNtNames[i_m]);

							// compare module name from list with known kernel module name
							if (RtlEqualUnicodeString(&usEnumModuleName, &usNtName, TRUE))
							{
								pModuleBase = (PVOID)Info->Modules[i].ImageBase;
								break;
							}
						}
					}
					else if (RtlEqualUnicodeString(&usModuleName, &usEnumModuleName, TRUE))
					{
						pModuleBase = (PVOID)Info->Modules[i].ImageBase;
					}

					RtlFreeUnicodeString(&usEnumModuleName);

					if (pModuleBase)
					{
						// module is found
						break;
					}
				}                    
			}                     

			RtlFreeUnicodeString(&usModuleName);
		}        

		ExFreePool(Info);
	}

	return pModuleBase;
}
//--------------------------------------------------------------------------------------
ULONG KernelGetExportAddress(PVOID Image, char *lpszFunctionName)
{
	__try
	{
		PIMAGE_EXPORT_DIRECTORY pExport = NULL;

		PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32)
			((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew);

		if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
		{
			// 32-bit image
			if (pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
			{
				pExport = (PIMAGE_EXPORT_DIRECTORY)RVATOVA(
					Image,
					pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
					);
			}                        
		}        
		else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
		{
			// 64-bit image
			PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64)
				((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew);

			if (pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
			{
				pExport = (PIMAGE_EXPORT_DIRECTORY)RVATOVA(
					Image,
					pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
					);
			}
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unkown machine type\n");
			return 0;
		}

		if (pExport)
		{
			PULONG AddressOfFunctions = (PULONG)RVATOVA(Image, pExport->AddressOfFunctions);
			PSHORT AddrOfOrdinals = (PSHORT)RVATOVA(Image, pExport->AddressOfNameOrdinals);
			PULONG AddressOfNames = (PULONG)RVATOVA(Image, pExport->AddressOfNames);
			ULONG i=0;
			for (i = 0; i < pExport->NumberOfFunctions; i++)
			{
				if (!strcmp((char *)RVATOVA(Image, AddressOfNames[i]), lpszFunctionName))
				{
					return AddressOfFunctions[AddrOfOrdinals[i]];
				}
			}
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, "WARNING: Export directory not found\n");
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		DbgMsg(__FILE__, __LINE__, __FUNCTION__"() EXCEPTION\n");
	}

	return NULL;
}
//--------------------------------------------------------------------------------------
POBJECT_NAME_INFORMATION GetObjectName(PVOID pObject)
{
	ULONG BuffSize = 0x100;
	POBJECT_NAME_INFORMATION ObjNameInfo;
	NTSTATUS ns;

	while (1)
	{
		if ((ObjNameInfo = (POBJECT_NAME_INFORMATION)ExAllocatePool(NonPagedPool, BuffSize)) == NULL)
			return FALSE;

		ns = ObQueryNameString(pObject, ObjNameInfo, BuffSize, &BuffSize);

		if (ns == STATUS_INFO_LENGTH_MISMATCH)
		{               
			ExFreePool(ObjNameInfo);
			BuffSize += 0x100;
		}
		else
			break;
	}

	if (NT_SUCCESS(ns))
	{
		return ObjNameInfo;
	} 

	if (ObjNameInfo)
		ExFreePool(ObjNameInfo);

	return NULL;    
}
//--------------------------------------------------------------------------------------
// get object name by its handle
POBJECT_NAME_INFORMATION GetObjectNameByHandle(HANDLE hObject)
{
	PVOID pObject;
	NTSTATUS ns;
	POBJECT_NAME_INFORMATION ObjNameInfo = NULL;

	ns = ObReferenceObjectByHandle(hObject, 0, 0, KernelMode, &pObject, NULL);
	if (NT_SUCCESS(ns))
	{
		ObjNameInfo = GetObjectName(pObject);
		ObDereferenceObject(pObject);
	} 
	else
		DbgMsg(__FILE__, __LINE__, "ObReferenceObjectByHandle() fails; status: 0x%.8x\n", ns);

	return ObjNameInfo;
}
//--------------------------------------------------------------------------------------
POBJECT_NAME_INFORMATION GetFullNtPath(PUNICODE_STRING Name)
{
	NTSTATUS ns;
	OBJECT_ATTRIBUTES ObjAttr;
	HANDLE hFile;
	IO_STATUS_BLOCK StatusBlock;
	POBJECT_NAME_INFORMATION ObjNameInf;

	InitializeObjectAttributes(&ObjAttr, Name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE , NULL, NULL);

	ns = ZwOpenFile(
		&hFile, 
		FILE_READ_DATA | SYNCHRONIZE, 
		&ObjAttr, 
		&StatusBlock, 
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
		FILE_SYNCHRONOUS_IO_NONALERT
		);
	if (!NT_SUCCESS(ns))
	{
		return NULL;
	}

	ObjNameInf = GetObjectNameByHandle(hFile);

	ZwClose(hFile);

	return ObjNameInf;
}
//--------------------------------------------------------------------------------------
BOOLEAN GetNormalizedModulePath(PANSI_STRING asPath, PANSI_STRING asNormalizedPath)
{
	BOOLEAN bRet = FALSE;
	ANSI_STRING asFullPath;
	UNICODE_STRING usPath;
	NTSTATUS ns;
	char *lpszWnd = "\\WINDOWS\\", *lpszNt = "\\WINNT\\", *lpszLetter = "\\SystemRoot\\";
	char *lpszDrivers = "system32\\drivers\\";

	if (!strncmp(asPath->Buffer, lpszWnd, min(strlen(lpszWnd), asPath->Length)) ||
		!strncmp(asPath->Buffer, lpszNt, min(strlen(lpszNt), asPath->Length)))
	{        
		ULONG Ptr = 0;
		ULONG FullPathLen;
		char *lpszFullPath;
		if (!strncmp(asPath->Buffer, lpszWnd, strlen(lpszWnd)))
		{
			Ptr = (ULONG)strlen(lpszWnd);
		}
		else if (!strncmp(asPath->Buffer, lpszNt, strlen(lpszNt)))
		{
			Ptr = (ULONG)strlen(lpszNt);
		}

		FullPathLen = asPath->Length - Ptr + strlen(lpszLetter) + 1;
		lpszFullPath = (char *)ExAllocatePool(NonPagedPool, FullPathLen);
		if (lpszFullPath)
		{
			RtlZeroMemory(lpszFullPath, FullPathLen);

			strcpy(lpszFullPath, lpszLetter);
			strncat(lpszFullPath, asPath->Buffer + Ptr, asPath->Length);            

			RtlInitAnsiString(&asFullPath, lpszFullPath);
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
			return FALSE;
		}
	}    
	else
	{
		asFullPath.Buffer = asPath->Buffer;
		asFullPath.Length = asPath->Length;
		asFullPath.MaximumLength = asPath->MaximumLength;
	}

	ns = RtlAnsiStringToUnicodeString(&usPath, &asFullPath, TRUE);
	if (NT_SUCCESS(ns))
	{
		POBJECT_NAME_INFORMATION ObjName = GetFullNtPath(&usPath);
		if (ObjName)
		{
			ns = RtlUnicodeStringToAnsiString(asNormalizedPath, &ObjName->Name, TRUE);
			if (NT_SUCCESS(ns))
			{
				bRet = TRUE;     
			}   
			else
			{
				DbgMsg(__FILE__, __LINE__, "RtlUnicodeStringToAnsiString() fails; status: 0x%.8x\n", ns);
			}

			ExFreePool(ObjName);
		}        

		RtlFreeUnicodeString(&usPath);
	}
	else
	{
		DbgMsg(__FILE__, __LINE__, "RtlAnsiStringToUnicodeString() fails; status: 0x%.8x\n", ns);
	}

	if (!bRet)
	{
		ULONG Offset = 0;
		ULONG i=0;
		for (i = 0; i < asFullPath.Length; i++)
		{
			if (asFullPath.Buffer[i] == '\\')
			{
				Offset = i + 1;
			}
		}

		if (Offset == 0)
		{
			ULONG FullPathLen = asFullPath.Length + strlen(lpszLetter) + strlen(lpszDrivers) + 1;
			char *lpszFullPath = (char *)ExAllocatePool(NonPagedPool, FullPathLen);
			if (lpszFullPath)
			{
				RtlZeroMemory(lpszFullPath, FullPathLen);

				strcpy(lpszFullPath, lpszLetter);
				strcat(lpszFullPath, lpszDrivers);
				strncat(lpszFullPath, asFullPath.Buffer, asFullPath.Length);            

				if (asFullPath.Buffer != asPath->Buffer)
				{
					RtlFreeAnsiString(&asFullPath);
				}

				RtlInitAnsiString(&asFullPath, lpszFullPath);

				ns = RtlAnsiStringToUnicodeString(&usPath, &asFullPath, TRUE);
				if (NT_SUCCESS(ns))
				{
					POBJECT_NAME_INFORMATION ObjName = GetFullNtPath(&usPath);
					if (ObjName)
					{
						ns = RtlUnicodeStringToAnsiString(asNormalizedPath, &ObjName->Name, TRUE);
						if (NT_SUCCESS(ns))
						{
							bRet = TRUE;     
						}   
						else
						{
							DbgMsg(__FILE__, __LINE__, "RtlUnicodeStringToAnsiString() fails; status: 0x%.8x\n", ns);
						}

						ExFreePool(ObjName);
					}        

					RtlFreeUnicodeString(&usPath);
				}
				else
				{
					DbgMsg(__FILE__, __LINE__, "RtlAnsiStringToUnicodeString() fails; status: 0x%.8x\n", ns);
				}
			}
			else
			{
				DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
			}
		}
	}

	if (asFullPath.Buffer != asPath->Buffer)
	{
		RtlFreeAnsiString(&asFullPath);
	}

	return bRet;
}
//--------------------------------------------------------------------------------------
PVOID GetSysInf(SYSTEM_INFORMATION_CLASS InfoClass)
{
	NTSTATUS ns;
	ULONG RetSize, Size = 0x100;
	PVOID Info;

	while (1) 
	{    
		if ((Info = ExAllocatePool(NonPagedPool, Size)) == NULL) 
		{
			DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
			return NULL;
		}

		RetSize = 0;
		ns = ZwQuerySystemInformation(InfoClass, Info, Size, &RetSize);
		if (ns == STATUS_INFO_LENGTH_MISMATCH)
		{       
			ExFreePool(Info);
			Info = NULL;

			if (RetSize > 0)
			{
				Size = RetSize + 0x100;
			}            
			else
				break;
		}
		else
			break;    
	}

	if (!NT_SUCCESS(ns))
	{
		DbgMsg(__FILE__, __LINE__, "ZwQuerySystemInformation() fails; status: 0x%.8x\n", ns);

		if (Info)
			ExFreePool(Info);

		return NULL;
	}

	return Info;
}
//--------------------------------------------------------------------------------------
BOOLEAN AllocUnicodeString(PUNICODE_STRING us, USHORT MaximumLength)
{
	ULONG ulMaximumLength = MaximumLength+2;

	if (MaximumLength > 0)
	{
		if ((us->Buffer = (PWSTR)ExAllocatePool(NonPagedPool, ulMaximumLength)) == NULL)
			return FALSE;

		RtlZeroMemory(us->Buffer, ulMaximumLength);

		us->Length = 0;
		us->MaximumLength = MaximumLength;

		return TRUE;
	}

	return FALSE;
}
//--------------------------------------------------------------------------------------
BOOLEAN AppendUnicodeToString(PUNICODE_STRING Dest, PCWSTR Source, USHORT Len)
{
	ULONG ulLen = Len;

	if (Dest->MaximumLength >= Dest->Length + Len)
	{
		RtlCopyMemory((PUCHAR)Dest->Buffer + Dest->Length, Source, ulLen);
		Dest->Length += Len;

		return TRUE;
	}

	return FALSE;
} 
//--------------------------------------------------------------------------------------
ULONG GetFileSize(HANDLE hFile, PULONG FileSizeHigh)
{
	FILE_STANDARD_INFORMATION FileStandard;
	IO_STATUS_BLOCK IoStatusBlock;

	NTSTATUS ns = ZwQueryInformationFile(
		hFile,
		&IoStatusBlock,
		&FileStandard,
		sizeof(FILE_STANDARD_INFORMATION),
		FileStandardInformation
		);
	if (!NT_SUCCESS(ns))
	{
		DbgMsg(__FILE__, __LINE__, "ZwQueryInformationFile() fails; status: 0x%.8x\n", ns);
		return -1;        
	}

	if (FileSizeHigh != NULL)
		*FileSizeHigh = FileStandard.EndOfFile.u.HighPart;

	return FileStandard.EndOfFile.u.LowPart;
} 
//--------------------------------------------------------------------------------------
BOOLEAN ReadFromFile(PUNICODE_STRING FileName, PVOID *Data, PULONG DataSize)
{
	BOOLEAN bRet = FALSE;
	NTSTATUS ns;
	OBJECT_ATTRIBUTES ObjAttr;
	HANDLE hFile;
	IO_STATUS_BLOCK StatusBlock;

	*Data = NULL;
	*DataSize = 0;

	InitializeObjectAttributes(&ObjAttr, FileName, 
		OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE , NULL, NULL);

	ns = ZwOpenFile(
		&hFile, 
		FILE_READ_DATA | SYNCHRONIZE, 
		&ObjAttr, 
		&StatusBlock, 
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
		FILE_SYNCHRONOUS_IO_NONALERT
		);
	if (NT_SUCCESS(ns))
	{
		ULONG FileSize = GetFileSize(hFile, NULL);
		if (FileSize > 0)
		{
			PVOID FileData = ExAllocatePool(NonPagedPool, FileSize);
			if (FileData)
			{
				RtlZeroMemory(FileData, FileSize);

				ns = ZwReadFile(hFile, 0, NULL, NULL, &StatusBlock, FileData, FileSize, 0, NULL);
				if (NT_SUCCESS(ns))
				{
					bRet = TRUE;
					*Data = FileData;
					*DataSize = FileSize;
				} 
				else 
				{
					DbgMsg(__FILE__, __LINE__, "ZwReadFile() fails; status: 0x%.8x\n", ns);
					ExFreePool(FileData);
				}
			}
			else
			{
				DbgMsg(__FILE__, __LINE__, "ExAllocatePool() fails\n");
			}
		}

		ZwClose(hFile);
	}  

	return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN DumpToFile(PUNICODE_STRING FileName, PVOID Data, ULONG DataSize)
{
	BOOLEAN bRet = FALSE;
	NTSTATUS ns;
	OBJECT_ATTRIBUTES ObjAttr;
	HANDLE hFile;
	IO_STATUS_BLOCK StatusBlock;

	InitializeObjectAttributes(&ObjAttr, FileName, 
		OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE , NULL, NULL);

	ns = ZwCreateFile(
		&hFile,
		FILE_ALL_ACCESS | SYNCHRONIZE,
		&ObjAttr,
		&StatusBlock,
		NULL,
		FILE_ATTRIBUTE_NORMAL,
		0,
		FILE_OVERWRITE_IF,
		FILE_SYNCHRONOUS_IO_NONALERT,
		NULL,
		0
		);
	if (NT_SUCCESS(ns))
	{
		ns = ZwWriteFile(hFile, NULL, NULL, NULL, &StatusBlock, Data, DataSize, NULL, NULL);
		if (NT_SUCCESS(ns))
		{
			bRet = TRUE;         
		} 
		else 
		{
			DbgMsg(__FILE__, __LINE__, "ZwWriteFile() fails; status: 0x%.8x\n", ns);
		}        

		ZwClose(hFile);
	}   

	return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN DeleteFile(PUNICODE_STRING usFileName)
{
	BOOLEAN bRet = FALSE;    
	OBJECT_ATTRIBUTES ObjAttr;
	IO_STATUS_BLOCK IoStatusBlock;
	HANDLE FileHandle;
	NTSTATUS ns;
	DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): '%wZ'\n", usFileName);

	InitializeObjectAttributes(&ObjAttr, usFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

	// open file
	ns = ZwCreateFile(
		&FileHandle,
		DELETE,
		&ObjAttr,
		&IoStatusBlock,
		NULL,
		FILE_ATTRIBUTE_NORMAL,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		FILE_OPEN,
		FILE_NON_DIRECTORY_FILE,
		NULL,
		0
		);    
	if (NT_SUCCESS(ns))
	{
		FILE_BASIC_INFORMATION FileBsicInfo;
		FILE_DISPOSITION_INFORMATION FileDispInfo;
		ns = ZwQueryInformationFile(
			FileHandle, 
			&IoStatusBlock,
			&FileBsicInfo,
			sizeof(FileBsicInfo),
			FileBasicInformation
			);
		if (NT_SUCCESS(ns))
		{
			// chenge file attributes to normal
			FileBsicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;

			ns = ZwSetInformationFile(
				FileHandle,
				&IoStatusBlock,
				&FileBsicInfo,
				sizeof(FileBsicInfo),
				FileBasicInformation
				);
			if (!NT_SUCCESS(ns))
			{
				DbgMsg(__FILE__, __LINE__, "ZwSetInformationFile() fails; status: 0x%.8x\n", ns);    
			}
		}     
		else
		{
			DbgMsg(__FILE__, __LINE__, "ZwQueryInformationFile() fails; status: 0x%.8x\n", ns);    
		}

		
		FileDispInfo.DeleteFile = TRUE;    

		// ... and delete it
		ns = ZwSetInformationFile(
			FileHandle,
			&IoStatusBlock,
			&FileDispInfo,
			sizeof(FILE_DISPOSITION_INFORMATION),
			FileDispositionInformation
			);
		if (!NT_SUCCESS(ns))
		{
			DbgMsg(__FILE__, __LINE__, "ZwSetInformationFile() fails; status: 0x%.8x\n", ns);    
		}
		else
			bRet = TRUE;

		ZwClose(FileHandle);
	}        
	else
	{
		DbgMsg(__FILE__, __LINE__, "ZwCreateFile() fails; status: 0x%.8x\n", ns);
	}

	return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN LoadImageAsDataFile(PUNICODE_STRING usName, PVOID *Image, PULONG MappedImageSize)
{    
	PVOID Data = NULL;
	ULONG DataSize = 0;

	if (ReadFromFile(usName, &Data, &DataSize))
	{
		PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32)
			((PUCHAR)Data + ((PIMAGE_DOS_HEADER)Data)->e_lfanew);

		PIMAGE_SECTION_HEADER pSection = NULL;

		ULONG ImageSize = 0, HeadersSize = 0, NumberOfSections = 0;

		if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
		{
			// 32-bit image
			pSection = (PIMAGE_SECTION_HEADER)
				(pHeaders32->FileHeader.SizeOfOptionalHeader + 
				(PUCHAR)&pHeaders32->OptionalHeader);

			ImageSize = pHeaders32->OptionalHeader.SizeOfImage;
			HeadersSize = pHeaders32->OptionalHeader.SizeOfHeaders;
			NumberOfSections = pHeaders32->FileHeader.NumberOfSections;           
		}        
		else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
		{
			// 64-bit image
			PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64)
				((PUCHAR)Data + ((PIMAGE_DOS_HEADER)Data)->e_lfanew);

			pSection = (PIMAGE_SECTION_HEADER)
				(pHeaders64->FileHeader.SizeOfOptionalHeader + 
				(PUCHAR)&pHeaders64->OptionalHeader);

			ImageSize = pHeaders64->OptionalHeader.SizeOfImage;
			HeadersSize = pHeaders64->OptionalHeader.SizeOfHeaders;
			NumberOfSections = pHeaders64->FileHeader.NumberOfSections;
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unkown machine type\n");
			ExFreePool(Data);
			return FALSE;
		}

		if (*Image = ExAllocatePool(NonPagedPool, ImageSize))
		{
			// copy headers
			ULONG i;
			RtlCopyMemory(*Image, Data, HeadersSize);

			// copy sections        
			for (i = 0; i < NumberOfSections; i++)
			{            
				RtlCopyMemory(
					(PUCHAR)*Image + pSection->VirtualAddress, 
					(PUCHAR)Data + pSection->PointerToRawData,
					min(pSection->SizeOfRawData, pSection->Misc.VirtualSize)
					);

				pSection++;
			}

			*MappedImageSize = ImageSize;

			return TRUE;
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, "ExAllocatePool() ERROR\n");
		}

		ExFreePool(Data);
	}

	return FALSE;
}
//--------------------------------------------------------------------------------------
BOOLEAN QuerySymbolicLink(PUNICODE_STRING usName, PUNICODE_STRING usDestination)
{
	BOOLEAN bRet = FALSE;
	HANDLE h = NULL;
	OBJECT_ATTRIBUTES ObjAttr;
	NTSTATUS ns;
	InitializeObjectAttributes(&ObjAttr, usName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, 0);            

	// open symbolic link by name
	ns = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &ObjAttr);
	if (NT_SUCCESS(ns))
	{
		PWSTR Buff =NULL;
		USHORT wSize = 255 * sizeof(WCHAR);
query:
		// get target object by symbolic link
		Buff = (PWSTR)M_ALLOC(wSize);
		if (Buff)
		{
			ULONG RetLen = 0;

			usDestination->Buffer = Buff;
			usDestination->Length = 0;
			usDestination->MaximumLength = wSize;

			ns = ZwQuerySymbolicLinkObject(h, usDestination, &RetLen);
			if (NT_SUCCESS(ns))
			{
				bRet = TRUE;
			}
			else if (ns == STATUS_BUFFER_TOO_SMALL)
			{
				M_FREE(Buff);
				wSize += 0x100;
				goto query;
			}
			else
			{
				M_FREE(Buff);
				DbgMsg(__FILE__, __LINE__, "NtQuerySymbolicLinkObject() fails; status: 0x%.8x\n", ns);
			}
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, "M_ALLOC() fails\n");
		}

		ZwClose(h);            
	}

	return bRet;
}
//--------------------------------------------------------------------------------------
#ifdef WP_STUFF
void __stdcall ClearWp(PVOID Param)
{
#ifdef _X86_
	__asm
	{              
		mov     eax,cr0             
			and     eax,not 000010000h
			mov     cr0,eax
	}
#else
	// clear wp-bit in cr0 register
	_clear_wp();
#endif // _X_86_
}
//--------------------------------------------------------------------------------------
void __stdcall SetWp(PVOID Param)
{
#ifdef _X86_
	__asm
	{
		mov     eax,cr0
			or      eax,000010000h
			mov     cr0,eax
	}
#else
	// set wp-bit in cr0 register
	_set_wp();
#endif // _X_86_
}
#endif // WP_STUFF
//--------------------------------------------------------------------------------------
typedef struct _PROCESSOR_THREAD_PARAM
{
	KAFFINITY Mask;
	PKSTART_ROUTINE Routine;
	PVOID Param;

} PROCESSOR_THREAD_PARAM,
	*PPROCESSOR_THREAD_PARAM;

NTKERNELAPI
	VOID
	KeSetSystemAffinityThread(
	KAFFINITY Affinity
	);

void NTAPI ProcessorThread(PVOID Param)
{
	PPROCESSOR_THREAD_PARAM ThreadParam = (PPROCESSOR_THREAD_PARAM)Param;

	// bind thread to specific processor
	KeSetSystemAffinityThread(ThreadParam->Mask);

	// execute payload on this processor
	ThreadParam->Routine(ThreadParam->Param);
}

void ForEachProcessor(PKSTART_ROUTINE Routine, PVOID Param)
{
	KAFFINITY i;
	KAFFINITY ActiveProcessors;
	if (KeGetCurrentIrql() > PASSIVE_LEVEL)
	{
		DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Invalid IRQL (Must be =PASSIVE_LEVEL)\n");
		return;
	}

	// get bitmask of active processors
	ActiveProcessors = KeQueryActiveProcessors();    

	for (i = 0; i < sizeof(KAFFINITY) * 8; i++)
	{
		KAFFINITY Mask = 1 << i;
		// check if this processor bit present in mask
		if (ActiveProcessors & Mask)
		{
			HANDLE hThread;
			PROCESSOR_THREAD_PARAM ThreadParam;
			NTSTATUS ns;
			ThreadParam.Mask    = Mask;
			ThreadParam.Param   = Param;
			ThreadParam.Routine = Routine;

			// create thread for this processor
			ns = PsCreateSystemThread(
				&hThread, 
				THREAD_ALL_ACCESS, 
				NULL, NULL, NULL, 
				ProcessorThread, 
				&ThreadParam
				);
			if (NT_SUCCESS(ns))
			{
				PVOID Thread;                
				// get pointer to thread object
				ns = ObReferenceObjectByHandle(
					hThread,
					THREAD_ALL_ACCESS,
					NULL,
					KernelMode,
					&Thread,
					NULL
					);
				if (NT_SUCCESS(ns))
				{
					// waiting for thread termination
					KeWaitForSingleObject(Thread, Executive, KernelMode, FALSE, NULL);
					ObDereferenceObject(Thread);
				}
				else
				{
					DbgMsg(__FILE__, __LINE__, "ObReferenceObjectByHandle() fails; status: 0x%.8x\n", ns);
				}                

				ZwClose(hThread);
			}
			else
			{
				DbgMsg(__FILE__, __LINE__, "PsCreateSystemThread() fails; status: 0x%.8x\n", ns);
			}
		}
	}
}
//--------------------------------------------------------------------------------------
ULONG GetSyscallNumber(char *lpszName)
{
	// get base address of ntdll.dll, that mapped into the system process
	PVOID NtdllBase = KernelGetModuleBase("ntdll.dll");
	if (NtdllBase)
	{
		// get function addres by name hash
		ULONG FuncRva = KernelGetExportAddress(NtdllBase, lpszName);
		if (FuncRva)
		{
			PUCHAR Func = (PUCHAR)NtdllBase + FuncRva;
#ifdef _X86_
			// check for mov eax,imm32
			if (*Func == 0xB8)
			{
				// return imm32 argument (syscall numbr)
				return *(PULONG)((PUCHAR)Func + 1);
			}
#elif _AMD64_
			// check for mov eax,imm32
			if (*(Func + 3) == 0xB8)
			{
				// return imm32 argument (syscall numbr)
				return *(PULONG)(Func + 4);
			}
#endif
		}   
	}    

	return -1;
}
//--------------------------------------------------------------------------------------
BOOLEAN RegQueryValueKey(HANDLE hKey, PWSTR lpwcName, ULONG Type, PVOID *Data, PULONG DataSize)
{
	BOOLEAN bRet = FALSE;
	PKEY_VALUE_FULL_INFORMATION ValueInformation = NULL;
	ULONG ResultLen = 0;
	UNICODE_STRING usValueName;
	NTSTATUS ns;
	RtlInitUnicodeString(&usValueName, lpwcName);

	if (Data && DataSize)
	{
		*Data = NULL;
		*DataSize = 0;
	}

	// get required buffer size
	 ns = ZwQueryValueKey(
		hKey, 
		&usValueName, 
		KeyValueFullInformation, 
		&ValueInformation, 
		0, 
		&ResultLen
		);
	if ((ns == STATUS_BUFFER_TOO_SMALL || 
		ns == STATUS_BUFFER_OVERFLOW) && ResultLen > 0)
	{
		// allocate memory for key information
		ValueInformation = (PKEY_VALUE_FULL_INFORMATION)M_ALLOC(ResultLen);
		if (ValueInformation)
		{
			memset(ValueInformation, 0, ResultLen);

			// query key information
			ns = ZwQueryValueKey(
				hKey,  
				&usValueName, 
				KeyValueFullInformation, 
				ValueInformation, 
				ResultLen, 
				&ResultLen
				);
			if (NT_SUCCESS(ns))
			{
				if (Type == REG_NONE || Type == ValueInformation->Type)
				{
					if (Data && DataSize)
					{
						// allocate memory for value data
						if (*Data = M_ALLOC(ValueInformation->DataLength))
						{
							RtlCopyMemory(
								*Data,
								(PUCHAR)ValueInformation + ValueInformation->DataOffset,
								ValueInformation->DataLength
								);

							*DataSize = ValueInformation->DataLength;
							bRet = TRUE;
						}
						else
						{
							DbgMsg(__FILE__, __LINE__, "M_ALLOC() fails\n");
						}
					}
					else
					{
						// just say about value existance
						bRet = TRUE;
					}                    
				}                
				else
				{
					DbgMsg(
						__FILE__, __LINE__, 
						__FUNCTION__"() ERROR: Bad value type (%d)\n",
						ValueInformation->Type
						);
				}
			}
			else
			{
				DbgMsg(__FILE__, __LINE__, "ZwQueryValueKey() fails; status: 0x%.8x\n", ns);
			}       

			M_FREE(ValueInformation);
		}
		else
		{
			DbgMsg(__FILE__, __LINE__, "M_ALLOC() fails\n");
		}
	}
	else
	{
		DbgMsg(__FILE__, __LINE__, "ZwQueryValueKey() fails; status: 0x%.8x\n", ns);
	}

	return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN RegSetValueKey(HANDLE hKey, PWSTR lpwcName, ULONG Type, PVOID Data, ULONG DataSize)
{
	UNICODE_STRING usValueName;
	NTSTATUS ns;
	RtlInitUnicodeString(&usValueName, lpwcName);
	ns = ZwSetValueKey(hKey, &usValueName, NULL, Type, Data, DataSize);
	if (!NT_SUCCESS(ns))
	{
		DbgMsg(__FILE__, __LINE__, "ZwSetValueKey() fails; status: 0x%.8x\n", ns);
		return FALSE;
	}

	return TRUE;
}
//--------------------------------------------------------------------------------------
#ifdef _X86_
#define PEB_PROCESS_PARAMS_OFFSET           0x10
#define PROCESS_PARAMS_FLAGS_OFFSET         0x08
#define PROCESS_PARAMS_IMAGE_NAME_OFFSET    0x38
#elif _AMD64_
#define PEB_PROCESS_PARAMS_OFFSET           0x20
#define PROCESS_PARAMS_FLAGS_OFFSET         0x08
#define PROCESS_PARAMS_IMAGE_NAME_OFFSET    0x60
#endif

#define	PROCESS_PARAMETERS_NORMALIZED	1	// pointers are absolute (not self-relative)

BOOLEAN GetProcessFullImagePath(PEPROCESS Process, PUNICODE_STRING ImagePath)
{
	BOOLEAN bRet = FALSE;
	HANDLE hProcess = NULL;
	NTSTATUS ns ;
	ns = ObOpenObjectByPointer(
		Process,
		OBJ_KERNEL_HANDLE,
		NULL,
		0,
		NULL,
		KernelMode,
		&hProcess
		);
	if (NT_SUCCESS(ns))
	{
		PROCESS_BASIC_INFORMATION ProcessInfo;    

		// get address of PEB
		ns = ZwQueryInformationProcess(
			hProcess,
			ProcessBasicInformation,
			&ProcessInfo,
			sizeof(ProcessInfo),
			NULL
			);
		if (NT_SUCCESS(ns))
		{
			KAPC_STATE ApcState;

			// change context to target process
			
			KeStackAttachProcess(Process, &ApcState);

			__try
			{
				PUCHAR Peb = (PUCHAR)ProcessInfo.PebBaseAddress;
				if (Peb)
				{
					// get pointer to RTL_USER_PROCESS_PARAMETERS
					PUCHAR ProcessParams = *(PUCHAR *)(Peb + PEB_PROCESS_PARAMS_OFFSET);
					if (ProcessParams)
					{
						// get image path
						PUNICODE_STRING ImagePathName = (PUNICODE_STRING)
							(ProcessParams + PROCESS_PARAMS_IMAGE_NAME_OFFSET);

						if (ImagePathName->Buffer && ImagePathName->Length > 0)
						{
							// allocate string
							if (AllocUnicodeString(ImagePath, ImagePathName->Length))
							{
								PWSTR lpwcName = NULL;
								ULONG Flags = *(PULONG)(ProcessParams + PROCESS_PARAMS_FLAGS_OFFSET);

								if (Flags & PROCESS_PARAMETERS_NORMALIZED)
								{
									// pointer to buffer is absolute address
									lpwcName = ImagePathName->Buffer;
								}
								else
								{
									// pointer to buffer is relative address
									lpwcName = (PWSTR)(ProcessParams + (ULONGLONG)ImagePathName->Buffer);
								}

								if (AppendUnicodeToString(ImagePath, lpwcName, ImagePathName->Length))
								{
									bRet = TRUE;
								}
								else
								{
									DbgMsg(__FILE__, __LINE__, "AppendUnicodeToString() ERROR\n");
								}
							}
							else
							{
								DbgMsg(__FILE__, __LINE__, "AllocUnicodeString() ERROR\n");
							}
						}
						else
						{
							DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ImageProcessName Is invaild\n");
						}
					}
					else
					{
						DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ProcessParams Is NULL\n");
					}
				}
				else
				{
					DbgMsg(__FILE__, __LINE__, __FUNCTION__"() PEB IS NULL\n");
				}
			}
			__except (EXCEPTION_EXECUTE_HANDLER)
			{
				DbgMsg(__FILE__, __LINE__, __FUNCTION__"() EXCEPTION\n");
			}

			KeUnstackDetachProcess(&ApcState);
		}
		else
		{
			// Can't query information about process, probably 'System' or rootkit activity
			DbgMsg(__FILE__, __LINE__, __FUNCTION__"() FailToGetPEB\n");
		}        

		ZwClose(hProcess);
	}
	else
	{
		DbgMsg(__FILE__, __LINE__, "ObOpenObjectByPointer() fails; status: 0x%.8x\n", ns);
	}

	return bRet;
}
//--------------------------------------------------------------------------------------
BOOLEAN AllocateUserMemory(ULONG Size, PMAPPED_MDL MdlInfo)
{
	PVOID Buffer;
	MdlInfo->Mdl = NULL;
	MdlInfo->Buffer = NULL;
	MdlInfo->MappedBuffer = NULL;

	// allocate kernel-mode buffer in non-paged pool
	Buffer = M_ALLOC(Size);
	if (Buffer)
	{
		// allocate memory descriptor
		PVOID MappedBuffer;
		PMDL Mdl = IoAllocateMdl(Buffer, Size, FALSE, FALSE, NULL);
		if (Mdl)
		{
			__try
			{
				// lock allocated pages
				MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
			}
			__except (EXCEPTION_EXECUTE_HANDLER)
			{
				DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): MmProbeAndLockPages() EXCEPTION\n");

				IoFreeMdl(Mdl);
				M_FREE(Buffer);

				return FALSE;
			}

			// map allocated pages into the user space
			 MappedBuffer = MmMapLockedPagesSpecifyCache(
				Mdl, 
				UserMode, 
				MmCached, 
				NULL, 
				FALSE, 
				NormalPagePriority
				);
			if (MappedBuffer)
			{
				MdlInfo->Mdl = Mdl;
				MdlInfo->Buffer = Buffer;
				MdlInfo->MappedBuffer = MappedBuffer;

				return TRUE;   
			}
			else
			{
				DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): MmMapLockedPagesSpecifyCache() fails\n");
			}

			MmUnlockPages(Mdl);
			IoFreeMdl(Mdl);
		} 
		else
		{
			DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): IoAllocateMdl() fails\n");
		}

		M_FREE(Buffer);
	}    
	else
	{
		DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): M_ALLOC() fails\n");
	}

	return FALSE;
}
//--------------------------------------------------------------------------------------
void FreeUserMemory(PMAPPED_MDL MdlInfo)
{
	// unmap user-mode address
	MmUnmapLockedPages(MdlInfo->MappedBuffer, MdlInfo->Mdl);

	// unlock pages
	MmUnlockPages(MdlInfo->Mdl);

	// free memory descriptor
	IoFreeMdl(MdlInfo->Mdl);

	// free buffer
	M_FREE(MdlInfo->Buffer);
}
//--------------------------------------------------------------------------------------
BOOLEAN IsWow64Process(PEPROCESS Process, BOOLEAN *bIsWow64)
{
	HANDLE hProcess = NULL;
	NTSTATUS ns;
	*bIsWow64 = FALSE;

	// get handle to target process
	 ns = ObOpenObjectByPointer(
		Process,
		OBJ_KERNEL_HANDLE,
		NULL,
		0,
		NULL,
		KernelMode,
		&hProcess
		);
	if (NT_SUCCESS(ns))
	{
		ULONG_PTR Wow64Info = NULL;

		ns = ZwQueryInformationProcess(
			hProcess,
			ProcessWow64Information,
			&Wow64Info,
			sizeof(Wow64Info),
			NULL
			);
		if (NT_SUCCESS(ns))
		{
			if (Wow64Info)
			{
				// this is wow64 process
				*bIsWow64 = TRUE;
			}
		}
		else 
		{
			DbgMsg(__FILE__, __LINE__, "ZwQueryInformationProcess() fails; status: 0x%.8x\n", ns);
		}

		ZwClose(hProcess);
		return TRUE;
	}
	else 
	{
		DbgMsg(__FILE__, __LINE__, "ObOpenObjectByPointer() fails; status: 0x%.8x\n", ns);
	}

	return FALSE;
}
//--------------------------------------------------------------------------------------
BOOLEAN AllocateKernelMemory(ULONG Size, PMAPPED_MDL MdlInfo)
{
	PVOID Buffer;
    MdlInfo->Mdl = NULL;
    MdlInfo->Buffer = NULL;
    MdlInfo->MappedBuffer = NULL;

    // allocate kernel-mode buffer in non-paged pool
    Buffer = M_ALLOC(Size);
    if (Buffer)
    {
        // allocate memory descriptor
        PMDL Mdl = IoAllocateMdl(Buffer, Size, FALSE, FALSE, NULL);
        if (Mdl)
        {
			PVOID MappedBuffer;
            __try
            {
                // lock allocated pages
                MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): MmProbeAndLockPages() EXCEPTION\n");

                IoFreeMdl(Mdl);
                M_FREE(Buffer);

                return FALSE;
            }

            // map allocated pages into the kernel space
            MappedBuffer = MmMapLockedPagesSpecifyCache(
                Mdl, 
                KernelMode, 
                MmCached, 
                NULL, 
                FALSE, 
                NormalPagePriority
            );
            if (MappedBuffer)
            {
                MdlInfo->Mdl = Mdl;
                MdlInfo->Buffer = Buffer;
                MdlInfo->MappedBuffer = MappedBuffer;

                return TRUE;   
            }
            else
            {
                DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): MmMapLockedPagesSpecifyCache() fails\n");
            }

            MmUnlockPages(Mdl);
            IoFreeMdl(Mdl);
        } 
        else
        {
            DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): IoAllocateMdl() fails\n");
        }

        M_FREE(Buffer);
    }    
    else
    {
        DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): M_ALLOC() fails\n");
    }

    return FALSE;
}
//--------------------------------------------------------------------------------------
void FreeKernelMemory(PMAPPED_MDL MdlInfo)
{
    // unmap user-mode address
    MmUnmapLockedPages(MdlInfo->MappedBuffer, MdlInfo->Mdl);

    // unlock pages
    MmUnlockPages(MdlInfo->Mdl);

    // free memory descriptor
    IoFreeMdl(MdlInfo->Mdl);

    // free buffer
    M_FREE(MdlInfo->Buffer);
}
//--------------------------------------------------------------------------------------
void CopyKernelMemory(PVOID Dst, PVOID Src, ULONG Size)
{
	ULONG i;
    for (i = 0; i < Size; i++)
    {
        if (MmIsAddressValid((PVOID)((PUCHAR)Src + i)))
        {
            *((PUCHAR)Dst + i) = *((PUCHAR)Src + i);
        }
    }
}
//--------------------------------------------------------------------------------------
BOOLEAN AllocatePteTable(PVOID *Address, PULONG PfnValue)
{
    ULONG PfnList[1];
    MAPPED_MDL MdlInfo;

    if (AllocateKernelMemory(PAGE_SIZE, &MdlInfo))
    {
		PHYSICAL_ADDRESS MemAddr;
        memset(MdlInfo.MappedBuffer, 0, PAGE_SIZE);

        MemAddr = MmGetPhysicalAddress(MdlInfo.MappedBuffer);

        *PfnValue = (ULONG)(MemAddr.QuadPart >> PAGE_SHIFT);
        *Address = MdlInfo.MappedBuffer;

        DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): "IFMT" (PFN=0x%.8x)\n", *Address, *PfnValue);

        return TRUE;
    }

    return FALSE;
}
//--------------------------------------------------------------------------------------
BOOLEAN SetPfnForPagePae(PVOID Pointer, PHYSICAL_ADDRESS *PhysicalAddr)
{
    MMPTE_PAE *Pde = MiGetPdeAddressPae(Pointer);

    if (Pde->u.Hard.Valid)
    {
        MMPTE_PAE *Pte = NULL;
        BOOLEAN bFakePte = FALSE;

        DbgMsg(
            __FILE__, __LINE__, 
            "PDE entry 0x%.8x is valid, PTE PFN=0x%.8x\n", 
            Pde, Pde->u.Hard.PageFrameNumber
        );

        if (Pde->u.Hard.LargePage != 0)
        {
            // This is a large 2M page
			PVOID NewTable = NULL;
			ULONG NewTablePfn = 0;
			ULONG PteNumber;
            DbgMsg(__FILE__, __LINE__, "[!] PDE entry 0x%.8x points to large 2M page\n", Pde);            
            // allocate new PTE table
            if (AllocatePteTable(&NewTable, &NewTablePfn))
            {
                /*
                    DIRTY HACK:
                    Convert one 2M page to 512 pages of 4K
                */
				ULONG i;
                for (i = 0; i < PAGE_SIZE / sizeof(ULONGLONG); i++)
                {
                    Pte = (MMPTE_PAE *)((PUCHAR)NewTable + i * sizeof(ULONGLONG));

                    Pte->u.Hard.Valid    = 1;
                    Pte->u.Hard.Global   = 1;
                    Pte->u.Hard.Write    = 1;
                    Pte->u.Hard.Dirty    = Pde->u.Hard.Dirty;
                    Pte->u.Hard.Accessed = Pde->u.Hard.Accessed;
                    
                    Pte->u.Hard.PageFrameNumber = Pde->u.Hard.PageFrameNumber + i;
                }

                __asm cli;

                Pde->u.Hard.PageFrameNumber = NewTablePfn;
                Pde->u.Hard.LargePage = 0;                

                __asm sti;

                bFakePte = TRUE;
            }
            else
            {
                return FALSE;
            }

            PteNumber = ((ULONG)Pointer & 0x001FF000) >> PAGE_SHIFT;
            Pte = (MMPTE_PAE *)((PUCHAR)NewTable + PteNumber * sizeof(ULONGLONG));
        }
        else
        {
            // Small 4K page, get its PTE
            Pte = MiGetPteAddressPae(Pointer);
        }

        if (Pte->u.Hard.Valid)
        {
            DbgMsg(
                __FILE__, __LINE__, 
                "PTE entry 0x%.8x is valid, PFN=0x%.8x\n", 
                Pte, Pte->u.Hard.PageFrameNumber
            );
                        
            if (!bFakePte)
            {
                PVOID NewTable = NULL;
                ULONG NewTablePfn = 0;

                // allocate new PTE table
                if (AllocatePteTable(&NewTable, &NewTablePfn))
                {
                    PVOID OldTable = (PVOID)((ULONG)Pte & 0xFFFFF000);
                    memcpy(NewTable, OldTable, PAGE_SIZE);

                    // set up faked PFN into the PTE entry
                    Pte = (MMPTE_PAE *)((ULONG)NewTable + ((ULONG)Pte & 0x00000FFF));
                    Pte->u.Hard.PageFrameNumber = (ULONG)(PhysicalAddr->QuadPart >> PAGE_SHIFT);

                    __asm cli;

                    // set up faked PTE table PFN into the PDE entry
                    Pde->u.Hard.PageFrameNumber = NewTablePfn;

                    __asm sti;
                }
                else
                {
                    return FALSE;
                }
            }
            else
            {
                __asm cli;

                // fake PTE table allready has been allocated
                Pte->u.Hard.PageFrameNumber = (ULONG)(PhysicalAddr->QuadPart >> 12);

                __asm sti;
            }            

            return TRUE;
        }
        else
        {
            // PTE is not valid
            DbgMsg(__FILE__, __LINE__, "PTE entry 0x%.8x is not valid\n", Pte);
        }
    }
    else
    {
        DbgMsg(__FILE__, __LINE__, "PDE entry 0x%.8x is not valid\n", Pde);
    }

    return FALSE;
}
//--------------------------------------------------------------------------------------
__declspec(naked) ULONG __stdcall GetCR4(void)
{    
    __asm 
    {
        // mov eax, cr4
        __emit  0x0F 
        __emit  0x20 
        __emit  0xE0
        ret
    }
}
//--------------------------------------------------------------------------------------
__declspec(naked) ULONG __stdcall GetCR3(void)
{    
    __asm 
    {
        // mov eax, cr3
        __emit  0x0F 
        __emit  0x20 
        __emit  0xD8
        ret
    }
}
//--------------------------------------------------------------------------------------
__declspec(naked) void __stdcall FlushTlbEntry(PVOID Address)
{    
    __asm 
    {
        mov     eax, Address
        invlpg  [eax]
        retn    4
    }
}
//--------------------------------------------------------------------------------------
BOOLEAN SetPfnsForAddress(PVOID Address, ULONG PagesCount, PHYSICAL_ADDRESS *PhysicalAddr)
{
	PHYSICAL_ADDRESS PdeAddr;
	KIRQL OldIrql;
	BOOLEAN bPaeEnabled = (GetCR4() & PAE_ON);   
	PHYSICAL_ADDRESS PhysicalAddrPage;
	ULONG i;
    // check for aligned page address
    if ((ULONG)Address % PAGE_SIZE != 0)
    {
        DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): Bad pointer passed\n");
        return FALSE;
    }

    PdeAddr = MmGetPhysicalAddress((PVOID)PDE_BASE_PAE);
    
    DbgMsg(
        __FILE__, __LINE__, 
        "Address: "IFMT" (Process="IFMT", PDE table is at 0x%.8x 0x%.8x`%.8x)\n", 
        Address, PsGetCurrentProcess(), PDE_BASE_PAE, PdeAddr.HighPart, PdeAddr.LowPart
    );

    KeSetSystemAffinityThread(0x00000001);


    PhysicalAddrPage.QuadPart = PhysicalAddr->QuadPart;

   
    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);

    // enumerate all needed pages
    for (i = 0; i < PagesCount; i++)
    {       
        PVOID Page = (PVOID)RVATOVA(Address, i * PAGE_SIZE);        

        if (bPaeEnabled) 
        {
            // query PTE PFN for single page
            if (!SetPfnForPagePae(Page, &PhysicalAddrPage))
            {
                KeLowerIrql(OldIrql);
                return FALSE;
            }
        }
        else
        {
            // only PAE mode suported
            KeLowerIrql(OldIrql);
            return FALSE;
        }

        PhysicalAddrPage.QuadPart += PAGE_SIZE;
    }       

    KeLowerIrql(OldIrql);

    ForEachProcessor(FlushTlbEntry, Address);

    return TRUE;
}

PVOID __stdcall GetCALLByName(PCWSTR SourceString)
{
	UNICODE_STRING DestinationString; // [sp+0h] [bp-8h]@1

	RtlInitUnicodeString(&DestinationString, SourceString);
	return MmGetSystemRoutineAddress(&DestinationString);
}

// EoF
PVOID
MiFindExportedRoutine (
					   IN PVOID DllBase,
					   BOOL ByName,
					   IN char *RoutineName,
					   DWORD Ordinal
							 )

							 /*++

							 Routine Description:

							 This function searches the argument module looking for the requested
							 exported function name.

							 Arguments:

							 DllBase - Supplies the base address of the requested module.

							 AnsiImageRoutineName - Supplies the ANSI routine name being searched for.

							 Return Value:

							 The virtual address of the requested routine or NULL if not found.

							 --*/

{
	USHORT OrdinalNumber;
	PULONG NameTableBase;
	PUSHORT NameOrdinalTableBase;
	PULONG AddressTableBase;
	PULONG Addr;
	LONG High;
	LONG Low;
	LONG Middle;
	LONG Result;
	ULONG ExportSize;
	PVOID FunctionAddress;
	PIMAGE_EXPORT_DIRECTORY ExportDirectory;

	PAGED_CODE();

	ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (
		DllBase,
		TRUE,
		IMAGE_DIRECTORY_ENTRY_EXPORT,
		&ExportSize);

	if (ExportDirectory == NULL) {
		return NULL;
	}

	//
	// Initialize the pointer to the array of RVA-based ansi export strings.
	//

	NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);

	//
	// Initialize the pointer to the array of USHORT ordinal numbers.
	//

	NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);

	AddressTableBase=(PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);

	if (!ByName)
	{
		return (PVOID)AddressTableBase[Ordinal];
	}

	//
	// Lookup the desired name in the name table using a binary search.
	//

	Low = 0;
	Middle = 0;
	High = ExportDirectory->NumberOfNames - 1;

	while (High >= Low) {

		//
		// Compute the next probe index and compare the import name
		// with the export name entry.
		//

		Middle = (Low + High) >> 1;

		Result = strcmp (RoutineName,
			(PCHAR)DllBase + NameTableBase[Middle]);

		if (Result < 0) {
			High = Middle - 1;
		}
		else if (Result > 0) {
			Low = Middle + 1;
		}
		else {
			break;
		}
	}

	//
	// If the high index is less than the low index, then a matching
	// table entry was not found. Otherwise, get the ordinal number
	// from the ordinal table.
	//

	if (High < Low) {
		return NULL;
	}

	OrdinalNumber = NameOrdinalTableBase[Middle];

	//
	// If the OrdinalNumber is not within the Export Address Table,
	// then this image does not implement the function.  Return not found.
	//

	if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
		return NULL;
	}

	//
	// Index into the array of RVA export addresses by ordinal number.
	//

	Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);

	FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

	//
	// Forwarders are not used by the kernel and HAL to each other.
	//

	ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) ||
		(FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

	return FunctionAddress;
}

PVOID GetKeServiceDescriptorTableShadow(PVOID NewBase)
{
	PUCHAR cPtr, pOpcode;
	ULONG Length;
	DWORD KeAddAddr;
	PVOID pRet=NULL;
	KeAddAddr = (DWORD)MiFindExportedRoutine(NewBase,TRUE,"KeAddSystemServiceTable",0);
	for (cPtr = (PUCHAR)KeAddAddr; 
		cPtr < (PUCHAR)KeAddAddr + PAGE_SIZE; 
		cPtr += Length) 
	{
		if (!MmIsAddressValid(cPtr)) break;

		Length = SizeOfCode(cPtr, &pOpcode);

		if (!Length || (Length == 1 && *pOpcode == 0xC3)) break;

		if (*(PUSHORT)pOpcode == 0x888D)
		{
			pRet = *(PVOID *)(pOpcode + 2);
			break;
		}
	}
	return pRet;
}
BOOLEAN ValidateUnicodeString(PUNICODE_STRING usStr)
{
	ULONG i;
	if (!MmIsAddressValid(usStr))
	{
		return FALSE;
	}

	if (usStr->Buffer == NULL || usStr->Length == 0)
	{
		return FALSE;
	}

	for (i = 0; i < usStr->Length; i++)
	{
		if (!MmIsAddressValid((PUCHAR)usStr->Buffer + i))
		{
			return FALSE;
		}
	}

	return TRUE;
}

WIN_VER_DETAIL GetWindowsVersion()
{
	UNICODE_STRING ustrFuncName = { 0 }; 
	RTL_OSVERSIONINFOEXW osverinfo = { sizeof(osverinfo) }; 
	PFN_RtlGetVersion pfnRtlGetVersion = NULL; 

	RtlInitUnicodeString(&ustrFuncName, L"RtlGetVersion"); 
	pfnRtlGetVersion = MmGetSystemRoutineAddress(&ustrFuncName); 

	if (pfnRtlGetVersion)
	{ 
		pfnRtlGetVersion((PRTL_OSVERSIONINFOW)&osverinfo); 
	} 
	else 
	{

		PsGetVersion(&osverinfo.dwMajorVersion, &osverinfo.dwMinorVersion, &osverinfo.dwBuildNumber, NULL);
	}

	// 	KdPrint(("[xxxxxxxx] OSVersion NT %d.%d:%d sp%d.%d\n", 
	// 		osverinfo.dwMajorVersion, osverinfo.dwMinorVersion, osverinfo.dwBuildNumber, 
	// 		osverinfo.wServicePackMajor, osverinfo.wServicePackMinor));

	if (osverinfo.dwMajorVersion == 5 && osverinfo.dwMinorVersion == 0) 
	{
		return WINDOWS_VERSION_2K;
	} 
	else if (osverinfo.dwMajorVersion == 5 && osverinfo.dwMinorVersion == 1) 
	{
		return WINDOWS_VERSION_XP;
	} 
	else if (osverinfo.dwMajorVersion == 5 && osverinfo.dwMinorVersion == 2) 
	{
		if (osverinfo.wServicePackMajor==0) 
		{ 
			return WINDOWS_VERSION_2K3;
		} 
		else 
		{
			return WINDOWS_VERSION_2K3_SP1_SP2;
		}
	} 
	else if (osverinfo.dwMajorVersion == 6 && osverinfo.dwMinorVersion == 0) //Vista+2008
	{
		return WINDOWS_VERSION_VISTA_2008;
	}
	else if (osverinfo.dwMajorVersion == 6 && osverinfo.dwMinorVersion == 1)
	{
		return WINDOWS_VERSION_7;
	}

	return WINDOWS_VERSION_NONE;
}
